/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.core;

/**
 * Hyperbolic transform implementation
 * 
 * @author Bernard Bou
 */
public class HyperTransform implements IHyperTransform
{
	// C O N S T A N T
	
	/**
	 * Null transform
	 */
	static public final HyperTransform NULLTRANSFORM = new HyperTransform(0., 0., 1., 0.);

	// D A T A

	/**
	 * Translation component, with abs(theXlat) < 1
	 */
	public HyperTranslation theXlat;

	/**
	 * Rotation componenet of transform, with abs(theRot) == 1
	 */
	public HyperRotation theRot;

	// C O N S T R U C T

	/**
	 * Default constructor that makes null transform
	 */
	public HyperTransform()
	{
		theXlat = new HyperTranslation(0., 0.);
		theRot = new HyperRotation(1., 0.);
	}

	/**
	 * Constructor from translation vector and rotation angle
	 * 
	 * @param p
	 *        translation vector
	 * @param r
	 *        rotation angle
	 */
	public HyperTransform(Complex p, Complex r)
	{
		theXlat = new HyperTranslation(p);
		theRot = new HyperRotation(r);
	}

	/**
	 * Constructor from translation, null rotation
	 * 
	 * @param p
	 *        translation vector
	 */
	public HyperTransform(HyperTranslation p)
	{
		theXlat = new HyperTranslation(p);
		theRot = new HyperRotation(1., 0.);
	}

	/**
	 * Constructor from rotation, null translation
	 * 
	 * @param r
	 *        rotation angle
	 */
	public HyperTransform(HyperRotation r)
	{
		theXlat = new HyperTranslation(0., 0.);
		theRot = r;
	}

	/**
	 * Constructor from translation and rotation
	 * 
	 * @param p
	 *        translation
	 * @param r
	 *        rotation
	 */
	public HyperTransform(HyperTranslation p, HyperRotation r)
	{
		// == set(HRotation.compose(r, p));
		theXlat = p;
		theRot = r;
	}

	/**
	 * Constructor for transform expressed in cartesian mode
	 * 
	 * @param px
	 *        x coordinate for translation vector expressed in cartesian mode (relative to 0,0)
	 * @param py
	 *        y coordinate for translation vector expressed in cartesian mode (relative to 0,0)
	 * @param rx
	 *        x coordinate for rotation expressed by point (relative to 0,0 and x-axis)
	 * @param ry
	 *        y coordinate for rotation expressed by point (relative to 0,0 and x-axis)
	 */
	public HyperTransform(double px, double py, double rx, double ry)
	{
		theXlat = new HyperTranslation(px, py);
		theRot = new HyperRotation(rx, ry);
	}

	/**
	 * Copy constructor
	 * 
	 * @param t
	 *        source transform
	 */
	public HyperTransform(HyperTransform t)
	{
		theXlat = new HyperTranslation(t.theXlat);
		theRot = new HyperRotation(t.theRot);
	}

	/**
	 * Copy transform
	 * 
	 * @param t
	 *        source transform
	 * @return this transform with value set to t
	 */
	public HyperTransform set(HyperTransform t)
	{
		theXlat.set(t.theXlat);
		theRot.set(t.theRot);
		return this;
	}

	/**
	 * Construct transform as composition of 2 transforms
	 * 
	 * @param t1
	 *        transform
	 * @param t2
	 *        transform
	 */
	public HyperTransform(HyperTranslation t1, HyperTranslation t2)
	{
		composeXlats(t1, t2);
	}

	// I N V E R S E

	/**
	 * Inverse
	 * 
	 * @return this transform with inverse value
	 */
	public HyperTransform inverse()
	{
		theRot.inverse();
		theXlat.mul(theRot).neg();
		return this;
	}

	// M A P
	// map() does hyperbolic motion:
	// rotation on t.theta over center and translation over t.theXlat;

	/* (non-Javadoc)
	 * @see jFamilyTree.kernel.IHTransform#map(jFamilyTree.kernel.Complex)
	 */
	public Complex map(Complex z)
	{
		// <BOUTHIER>
		// rigid transformation of the hyperbolic plane
		// z = (z*theta+p)/(1+(~p)*z*theta)
		// </BOUTHIER>

		Complex num = new Complex(z).mul(theRot).add(theXlat);
		Complex denom = new Complex(theXlat).conj().mul(z).mul(theRot).add(Complex.ONE);
		return z.div(num, denom);

		// OPTIMIZED 
		// see HTOptimizedTransform which does just this 
		// factor out theta (1/theta = invtheta) 
		// z = (z*theta+p)/(1+(~p)*theta*z) 
		// z = theta(z+p*invtheta)/theta(invtheta+z*~p) 
		// z = (z+p*invtheta)/(invtheta+z*~p) 
		// invtheta can be precomputed 
		// p*invtheta can be precomputed (pontheta) 
		// z = (z+pontheta)/(invtheta+z*~p) 
		// at runtime it boils down to : 
		// -2 complex additions 
		// -1 complex multiplication 
		// -1 complex division
	}

	// C O M P O S E
	/**
	 * Compose this transform with other transform
	 * 
	 * @param t2
	 *        transform
	 * @return this transform as the result of composing the initial transform with t2
	 */
	public HyperTransform compose(HyperTransform t2)
	{
		// t1=this transform
		// thistransform is applied first
		// t1 o t2
		// (t1 o t2)(x) = t2(t1(x))
		// ~p=conjugate of p
		// p = (theta2*p1+p2) /(1+theta2*p1*~p2);
		// theta = (theta1*theta2+theta1*~p1*p2) /(1+theta2*p1*~p2);
		// = theta1*(theta2+~p1*p2) /(1+theta2*p1*~p2); (factored)

		// denominator : theta2*p1*~p2+1
		// ~p2 * p1 * theta2 + 1
		Complex denom = new Complex(t2.theXlat).conj().mul(theXlat).mul(t2.theRot).add(Complex.ONE);

		// translation
		// p1 * theta2 + p2
		Complex nom1 = new Complex(theXlat).mul(t2.theRot).add(t2.theXlat); // (theta2*p1)+p2

		// rotation
		// ~p1 * p2 + theta2 * theta1
		Complex nom2 = new Complex(theXlat).conj().mul(t2.theXlat).add(t2.theRot).mul(theRot); // theta1*(theta2+~p1*p2)

		// make
		theXlat.div(nom1, denom);
		theRot.div(nom2, denom);
		// theRot.normalize();

		return this;
	}

	/**
	 * Compose 2 translations
	 * 
	 * @param t1
	 *        translation
	 * @param t2
	 *        translation
	 * @return composition of t1 and t2
	 */
	public HyperTransform composeXlats(HyperTranslation t1, HyperTranslation t2)
	{
		set(HyperTranslation.compose(t1, t2));
		return this;
	}

	// S T R I N G

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		return "p=" + theXlat.toString() + " theta=" + theRot.toString();
	}
}
